package com.mixsmart.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.mixsmart.constant.IMixConstant;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * JSON工具类
 * @author 乌草坡
 * @since 2.5.8
 */
public class JSONUtils {

    private static final ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        addDateTimeModule(mapper);
    }
    
    /**
     * 将对象转换为json字符串
     * @param object 需要转换的对象
     * @return 返回json字符串
     */
    public static String toJson(Object object) {
        return toJson(object, null);
    }

    /**
     * 添加日期时间的处理模块
     * @param objectMapper ObjectMapper对象
     */
    public static void addDateTimeModule(ObjectMapper objectMapper) {
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(IMixConstant.DEFAULT_TIME_FORMAT)));
        objectMapper.setDateFormat(new SimpleDateFormat(IMixConstant.DEFAULT_DATE_TIME_FORMAT));
        objectMapper.registerModule(javaTimeModule);
    }

    private static ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        addDateTimeModule(mapper);
        return mapper;
    }

    /**
     * 将对象转换为json字符串
     * @param object 需要转换的对象
     * @param consumer 函数式接口
     * @return 返回json字符串
     */
    public static String toJson(Object object, Consumer<ObjectMapper> consumer) {
        if(object == null) return "";
        if(object instanceof String) {
            return object.toString();
        }
        ObjectMapper newMapper = mapper;
        try {
            if(null != consumer) {
                newMapper = createObjectMapper();
                consumer.accept(newMapper);
            }
            newMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                    gen.writeString("");
                }
            });
            return newMapper.writeValueAsString(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将JSON字符串转化为Map列表
     * @param jsonStr json字符串
     * @param consumer 用于设置转化参数的函数式接口；
     * @return 返回Map列表
     */
    public static List<Map<String, Object>> toListMap(String jsonStr, Consumer<ObjectMapper> consumer) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        ObjectMapper newMapper = mapper;
        try {
            if(null != consumer) {
                newMapper = createObjectMapper();
                consumer.accept(newMapper);
            }
            if(StringUtils.isNotEmpty(jsonStr) && !jsonStr.trim().startsWith(",")) {
                return newMapper.readValue(jsonStr, new TypeReference<List<Map<String, Object>>>(){});
            } else {
                return null;
            }
        } catch (JacksonException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将JSON字符串转化为Map列表
     * @param jsonStr json字符串
     * @return 返回Map列表
     */
    public static List<Map<String, Object>> toListMap(String jsonStr) {
        return toListMap(jsonStr, null);
    }

    /**
     * 根据指定类型解析json字符串，并返回该类型对象的列表
     * @param jsonStr json字符串
     * @param clazz 目标对象类型
     * @return 返回目标对象的列表
     */
    public static <T> List<T> toList(String jsonStr, Class<T> clazz) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            if(StringUtils.isNotEmpty(jsonStr) && !jsonStr.trim().startsWith(",")) {
                return mapper.readValue(jsonStr, new TypeReference<List<T>>(){});
            } else {
                return null;
            }
        } catch (JacksonException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据指定类型解析json字符串，并返回该类型的对象列表
     * @param jsonStr json字符串
     * @return 列表对象
     */
    public static List<Object> toList(String jsonStr) {
        return toList(jsonStr, Object.class);
    }

    /**
     * 根据指定类型解析json字符串，并返回该类型的对象
     * @param jsonStr json字符串
     * @param clazz 目标对象类型
     * @return 目标对象
     */
    public static <T> T fromJson(String jsonStr, Class<T> clazz) {
        return fromJson(jsonStr, clazz, null);
    }

    /**
     * 根据指定类型解析json字符串，并返回该类型的对象
     * @param jsonStr json字符串
     * @param clazz 目标对象类型
     * @param valueTypeRef 泛型对象
     * @return 目标对象
     */
    private static <T> T fromJson(String jsonStr, Class<T> clazz, TypeReference<T> valueTypeRef) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            if(StringUtils.isNotEmpty(jsonStr) && !jsonStr.trim().startsWith(",")) {
                if(null != clazz) {
                    return mapper.readValue(jsonStr, clazz);
                } else if(null != valueTypeRef) {
                    return mapper.readValue(jsonStr, valueTypeRef);
                } else {
                    return null;
                }
            } else {
                return null;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据指定类型解析json字符串，并返回该类型的对象
     * @param jsonStr json字符串
     * @param valueTypeRef 泛型类型对象
     * @return 目标对象
     */
    public static <T> T fromJson(String jsonStr, TypeReference<T> valueTypeRef) {
        return fromJson(jsonStr, null, valueTypeRef);
    }
    
    /**
     * json字符串解析为Map对象
     * @param jsonStr json字符串
     * @return Map对象
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> toMap(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            return mapper.readValue(jsonStr, Map.class);
        } catch (JacksonException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * map对象转换为对象
     * @param map map对象
     * @param clazz 需要转换的对象类型
     * @param <E> 对象类型
     * @return 返回转换后的对象
     */
    public static <E> E map2Object(Map<String, Object> map, Class<? extends E> clazz) {
        if(null == map || map.isEmpty() || null == clazz) {
            return null;
        }
        String jsonStr = toJson(map);
        if(StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        return fromJson(jsonStr, clazz);
    }

    /**
     * 判断是否为JSON格式的字符串
     * @param value 字符串值
     * @return 如果是JSON格式的字符串，返回true；否则返回：false
     */
    public static boolean isJson(String value) {
        value = StringUtils.handleNull(value);
        if(StringUtils.isEmpty(value)) {
            return false;
        }
        return ((value.startsWith("{") && value.endsWith("}")) || (value.startsWith("[") && value.endsWith("]")));
    }

    /**
     * 判断JSON字符串是否为数组
     * @param value json字符串
     * @return 如果是数组JSON则返回true；否则返回：false
     */
    public static boolean isArray(String value) {
        value = StringUtils.handleNull(value);
        if(StringUtils.isEmpty(value)) {
            return false;
        }
        return (value.startsWith("[") && value.endsWith("]"));
    }

    /**
     * 判断JSON字符串是否为对象
     * @param value json字符串
     * @return 如果是JSON对象则返回true；否则返回：false
     */
    public static boolean isObject(String value) {
        value = StringUtils.handleNull(value);
        if(StringUtils.isEmpty(value)) {
            return false;
        }
        return (value.startsWith("{") && value.endsWith("}"));
    }

    /**
     * 判断是否数组对象
     * @param value json字符串
     * @return 如果是数组对象则返回true；否则返回：false
     */
    public static boolean isArrayObject(String value) {
        value = StringUtils.handleNull(value);
        if(StringUtils.isEmpty(value)) {
            return false;
        }
        value  = value.replaceAll("\\s+", "");
        return (value.startsWith("[{") && value.endsWith("}]"));
    }



    /**
     * 移除JSON字符串中的转义符
     * @param value JSON字符串内容
     * @return 返回移除转义符后的JSON字符串
     */
    public static String removeJsonEscape(String value) {
        value = StringUtils.handleNull(value);
        if(StringUtils.isEmpty(value)) {
            return value;
        }
        value = value.replaceAll("\\\\\"", "\"");
        value = value.replaceAll(":\"\\{", ":{");
        value = value.replaceAll("}\"", "}");
        value = value.replaceAll(":\"\\[", ":[");
        value = value.replaceAll("]\"", "]");
        return value;
    }

    /**
     * 判断JSON内容是否为空
     * @param value JSON字符串
     * @return 如果为空返回true；否则返回：false
     */
    public static boolean isEmpty(String value) {
        return StringUtils.isEmpty(value) || "{}".equals(value) || "[]".equals(value) || "[{}]".equals(value);
    }
}
